package com.foftact.stockpe;

import com.foftact.stockpe.mapper.StockPEMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 股票估值服务实现类
 * 实现股票估值数据的抓取、处理和存储
 */
@Service
public class StockPEServiceImpl implements StockPEService {
    private static final Logger logger = LoggerFactory.getLogger(StockPEServiceImpl.class);
    private static final int RECENT_DATA_LIMIT = 10;
    private static final double PE_TTM_CHANGE_THRESHOLD = 0.0001; // PE_TTM变化阈值

    @Autowired
    private StockPEWebParser stockPEWebParser;

    @Autowired
    private StockPEMapper stockPEMapper;

    /**
     * 根据证券代码抓取并保存股票估值数据
     * @param securityCode 证券代码
     * @return 处理结果消息
     */
    @Override
    @Transactional
    public String fetchAndSaveStockPEData(String securityCode) {
        if (securityCode == null || securityCode.isEmpty()) {
            return "错误：证券代码不能为空";
        }

        logger.info("开始抓取股票估值数据，证券代码: {}", securityCode);
        try {
            // 从网页抓取数据
            StockPEWebDTO webDTO = stockPEWebParser.parseStockPEDataFromWeb(securityCode);
            if (webDTO == null) {
                return "错误：抓取股票估值数据失败";
            }

            // 查询最近10条数据
            List<StockPE> recentDataList = stockPEMapper.selectRecentByCodeAndLimit(securityCode, RECENT_DATA_LIMIT);
            //recentDataList 转成 map  key = getTradeDate
            Map<Date, StockPE> recentDataMap = recentDataList.stream()
                    .collect(Collectors.toMap(StockPE::getTradeDate, Function.identity()));

            // 将抓取的数据转换为数据库实体
            StockPE newStockPE = convertToStockPE(webDTO);
            boolean isUpdated = false;
            boolean isInserted = false;

            // 检查是否已有相同日期的数据
            boolean dataExists = false;
            if(recentDataMap.containsKey(newStockPE.getTradeDate())) {
                dataExists = true;
                StockPE existingData = recentDataMap.get(newStockPE.getTradeDate());
                // 检查数据一致性
                if (!compareDataConsistency(existingData, newStockPE)) {
                    // 数据不一致，更新数据
                    newStockPE.setId(existingData.getId());
//                        newStockPE.setUpdateCount(existingData.getUpdateCount() + 1);
                    int updated = stockPEMapper.updateByCodeAndTradeDate(newStockPE);
                    isUpdated = updated > 0;
                    logger.info("更新现有数据，证券代码: {}, 交易日期: {}", securityCode, newStockPE.getTradeDate());
                }
            }

            // 如果没有相同日期的数据，检查是否需要插入
            if (!dataExists) {
                // 检查最近一条数据的PE_TTM是否有变化
                if (!recentDataList.isEmpty()) {
                    StockPE latestData = recentDataList.get(0);
                    if (!comparePeTTM(latestData.getPeTTM(), newStockPE.getPeTTM())) {
                        // PE_TTM有变化，插入新数据
                        stockPEMapper.insert(newStockPE);
                        isInserted = true;
                        logger.info("插入新数据，证券代码: {}, 交易日期: {}", securityCode, newStockPE.getTradeDate());
                    }
                } else {
                    // 没有历史数据，直接插入
                    stockPEMapper.insert(newStockPE);
                    isInserted = true;
                    logger.info("插入新数据，证券代码: {}, 交易日期: {}", securityCode, newStockPE.getTradeDate());
                }
            }

            // 构建返回消息
            if (isUpdated) {
                return "成功：更新了股票估值数据";
            } else if (isInserted) {
                return "成功：插入了股票估值数据";
            } else {
                return "提示：数据完全一致，无需更新";
            }
        } catch (Exception e) {
            logger.error("抓取并保存股票估值数据失败，证券代码: {}", securityCode, e);
            return "错误：" + e.getMessage();
        }
    }

    /**
     * 批量抓取并保存多条股票估值数据
     * @param securityCodes 证券代码列表
     * @return 处理结果消息
     */
    @Override
    public String batchFetchAndSaveStockPEData(List<String> securityCodes) {
        if (securityCodes == null || securityCodes.isEmpty()) {
            return "错误：证券代码列表不能为空";
        }

        int successCount = 0;
        int failCount = 0;
        StringBuilder resultMessage = new StringBuilder();

        for (String securityCode : securityCodes) {
            try {
                String result = fetchAndSaveStockPEData(securityCode);
                if (result.startsWith("成功") || result.startsWith("提示")) {
                    successCount++;
                } else {
                    failCount++;
                    resultMessage.append(String.format("%s: %s\n", securityCode, result));
                }
                // 添加间隔，避免请求过于频繁
                TimeUnit.SECONDS.sleep(2);
            } catch (Exception e) {
                failCount++;
                resultMessage.append(String.format("%s: 处理异常 - %s\n", securityCode, e.getMessage()));
                logger.error("批量处理股票估值数据失败，证券代码: {}", securityCode, e);
            }
        }

        resultMessage.insert(0, String.format("批量处理完成：成功 %d 条，失败 %d 条\n", successCount, failCount));
        return resultMessage.toString();
    }

    /**
     * 根据证券代码和日期范围查询股票估值数据
     * @param securityCode 证券代码
     * @param startDate 开始日期
     * @param endDate 结束日期
     * @return 股票估值数据列表
     */
    @Override
    public List<StockPE> queryStockPEDataByCodeAndDateRange(String securityCode, Date startDate, Date endDate) {
        return stockPEMapper.selectByCodeAndDateRange(securityCode, startDate, endDate);
    }

    /**
     * 查询指定证券的最新股票估值数据
     * @param securityCode 证券代码
     * @return 股票估值数据
     */
    @Override
    public StockPE queryLatestStockPEData(String securityCode) {
        return stockPEMapper.selectLatestByCode(securityCode);
    }

    /**
     * 查询指定证券的最近N条股票估值数据
     * @param securityCode 证券代码
     * @param limit 查询数量限制
     * @return 股票估值数据列表
     */
    @Override
    public List<StockPE> queryRecentStockPEData(String securityCode, int limit) {
        return stockPEMapper.selectRecentByCodeAndLimit(securityCode, limit);
    }

    /**
     * 删除指定证券的所有股票估值数据
     * @param securityCode 证券代码
     * @return 影响行数
     */
    @Override
    @Transactional
    public int deleteStockPEDataByCode(String securityCode) {
        return stockPEMapper.deleteByCode(securityCode);
    }

    /**
     * 清空所有股票估值数据
     * @return 影响行数
     */
    @Override
    @Transactional
    public int clearAllStockPEData() {
        return stockPEMapper.truncateTable();
    }

    /**
     * 按周分组统计股票估值数据
     * @param securityCode 证券代码
     * @return 按周分组的数据列表
     */
    @Override
    public List<StockPE> groupDataByWeek(String securityCode) {
        // 这里可以实现按周分组的业务逻辑
        // 暂时返回最近10条数据
        return stockPEMapper.selectRecentByCodeAndLimit(securityCode, RECENT_DATA_LIMIT);
    }

    /**
     * 将WebDTO转换为StockPE实体
     * @param webDTO 网页数据传输对象
     * @return 股票估值实体
     */
    private StockPE convertToStockPE(StockPEWebDTO webDTO) {
        StockPE stockPE = new StockPE();
        stockPE.setSecurityCode(webDTO.getSecurityCode());
        stockPE.setSecurityName(webDTO.getSecurityName());
        stockPE.setTradeDate(webDTO.getTradeDate());
        stockPE.setPeTTM(webDTO.getPeTTM());
        stockPE.setRoeTTM(webDTO.getRoeTTM());
        stockPE.setRoeTTMLast2(webDTO.getRoeTTMLast2());
        stockPE.setRoeTTMLast5(webDTO.getRoeTTMLast5());
        stockPE.setRoeTTMQuarterly(webDTO.getRoeTTMQuarterly());
        stockPE.setRoeTTMYearly(webDTO.getRoeTTMYearly());
        
        // 设置近8个季度的原始ROE数据
        stockPE.setRoeLast1Rate(webDTO.getRoeLast1Rate());
        stockPE.setRoeLast1Date(webDTO.getRoeLast1Date());
        stockPE.setRoeLast2Rate(webDTO.getRoeLast2Rate());
        stockPE.setRoeLast2Date(webDTO.getRoeLast2Date());
        stockPE.setRoeLast3Rate(webDTO.getRoeLast3Rate());
        stockPE.setRoeLast3Date(webDTO.getRoeLast3Date());
        stockPE.setRoeLast4Rate(webDTO.getRoeLast4Rate());
        stockPE.setRoeLast4Date(webDTO.getRoeLast4Date());
        stockPE.setRoeLast5Rate(webDTO.getRoeLast5Rate());
        stockPE.setRoeLast5Date(webDTO.getRoeLast5Date());
        stockPE.setRoeLast6Rate(webDTO.getRoeLast6Rate());
        stockPE.setRoeLast6Date(webDTO.getRoeLast6Date());
        stockPE.setRoeLast7Rate(webDTO.getRoeLast7Rate());
        stockPE.setRoeLast7Date(webDTO.getRoeLast7Date());
        stockPE.setRoeLast8Rate(webDTO.getRoeLast8Rate());
        stockPE.setRoeLast8Date(webDTO.getRoeLast8Date());
        
//        stockPE.setUpdateCount(1);
        stockPE.setCreateTime(new Date());
        stockPE.setUpdateTime(new Date());
        return stockPE;
    }

    /**
     * 比较两条数据的一致性
     * @param existingData 现有数据
     * @param newData 新数据
     * @return 是否一致
     */
    private boolean compareDataConsistency(StockPE existingData, StockPE newData) {
        // 比较关键字段是否一致
        if (!safeEquals(existingData.getPeTTM(), newData.getPeTTM())) return false;
        if (!safeEquals(existingData.getRoeTTM(), newData.getRoeTTM())) return false;
        if (!safeEquals(existingData.getRoeTTMLast2(), newData.getRoeTTMLast2())) return false;
        if (!safeEquals(existingData.getRoeTTMLast5(), newData.getRoeTTMLast5())) return false;
        if (!safeEquals(existingData.getRoeTTMQuarterly(), newData.getRoeTTMQuarterly())) return false;
        if (!safeEquals(existingData.getRoeTTMYearly(), newData.getRoeTTMYearly())) return false;
//        if (!safeEquals(existingData.getQuarterDates(), newData.getQuarterDates())) return false;
        
        // 比较近8个季度的原始ROE数据
        if (!safeEquals(existingData.getRoeLast1Rate(), newData.getRoeLast1Rate())) return false;
        if (!safeEquals(existingData.getRoeLast1Date(), newData.getRoeLast1Date())) return false;
        if (!safeEquals(existingData.getRoeLast2Rate(), newData.getRoeLast2Rate())) return false;
        if (!safeEquals(existingData.getRoeLast2Date(), newData.getRoeLast2Date())) return false;
        if (!safeEquals(existingData.getRoeLast3Rate(), newData.getRoeLast3Rate())) return false;
        if (!safeEquals(existingData.getRoeLast3Date(), newData.getRoeLast3Date())) return false;
        if (!safeEquals(existingData.getRoeLast4Rate(), newData.getRoeLast4Rate())) return false;
        if (!safeEquals(existingData.getRoeLast4Date(), newData.getRoeLast4Date())) return false;
        if (!safeEquals(existingData.getRoeLast5Rate(), newData.getRoeLast5Rate())) return false;
        if (!safeEquals(existingData.getRoeLast5Date(), newData.getRoeLast5Date())) return false;
        if (!safeEquals(existingData.getRoeLast6Rate(), newData.getRoeLast6Rate())) return false;
        if (!safeEquals(existingData.getRoeLast6Date(), newData.getRoeLast6Date())) return false;
        if (!safeEquals(existingData.getRoeLast7Rate(), newData.getRoeLast7Rate())) return false;
        if (!safeEquals(existingData.getRoeLast7Date(), newData.getRoeLast7Date())) return false;
        if (!safeEquals(existingData.getRoeLast8Rate(), newData.getRoeLast8Rate())) return false;
        if (!safeEquals(existingData.getRoeLast8Date(), newData.getRoeLast8Date())) return false;
        
        return true;
    }

    /**
     * 比较PE_TTM是否有变化
     * @param oldPeTTM 旧的PE_TTM值
     * @param newPeTTM 新的PE_TTM值
     * @return 是否相同
     */
    private boolean comparePeTTM(BigDecimal oldPeTTM, BigDecimal newPeTTM) {
        if (oldPeTTM == null && newPeTTM == null) {
            return true;
        }
        if (oldPeTTM == null || newPeTTM == null) {
            return false;
        }
        // 使用阈值比较，避免精度问题
        return Math.abs(oldPeTTM.subtract(newPeTTM).doubleValue()) < PE_TTM_CHANGE_THRESHOLD;
    }

    /**
     * 安全比较两个对象是否相等
     * @param o1 对象1
     * @param o2 对象2
     * @return 是否相等
     */
    private boolean safeEquals(Object o1, Object o2) {
        if (o1 == null && o2 == null) {
            return true;
        }
        if (o1 == null || o2 == null) {
            return false;
        }
        return o1.equals(o2);
    }
}